package net.ufrog.common.jetbrick;

import jetbrick.template.JetEngine;
import jetbrick.template.JetTemplate;
import net.ufrog.common.Logger;
import net.ufrog.common.app.App;
import net.ufrog.common.cache.Caches;
import net.ufrog.common.exception.ServiceException;
import net.ufrog.common.utils.Codecs;
import net.ufrog.common.utils.Files;
import net.ufrog.common.utils.Objects;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;

/**
 * @author ultrafrog
 * @version 1.0, 2014-10-03
 * @since 1.0
 */
public class Templates {

    private static String uuid = Codecs.uuid();
    private static Map<String, JetTemplate> mJetTemplate = new HashMap<>();

    /** 构造函数 */
    private Templates() {}

    /**
     * 读取模板
     *
     * @param key 键值
     * @param source 内容
     * @return 模板
     */
    public static JetTemplate getTemplate(String key, String source) {
        if (!isCached(key)) {
            Properties config = App.properties("jetx.config", ".", Boolean.FALSE);
            JetEngine jetEngine = (config != null && config.size() > 0) ? JetEngine.create(config) : JetEngine.create();

            mJetTemplate.put(key, jetEngine.createTemplate(source));
            JetTemplateKey.get().addKey(key);
            Logger.info("initialize template '%s'", key);
        }
        return mJetTemplate.get(key);
    }

    /**
     * 渲染
     *
     * @param key 键值
     * @param source 内容
     * @param out 输出流
     * @param args 参数
     */
    public static void render(String key, String source, Writer out, Map<String, Object> args) {
        JetTemplate template = getTemplate(key, source);
        template.render(args, out);
    }

    /**
     * 渲染
     *
     * @param key 键值
     * @param source 内容
     * @param out 输出流
     * @param args 参数
     */
    public static void render(String key, String source, Writer out, Object... args) {
        render(key, source, out, Objects.map(args));
    }

    /**
     * 渲染
     *
     * @param key 键值
     * @param source 内容
     * @param args 参数
     * @return 渲染后模板字符串
     */
    public static String render(String key, String source, Map<String, Object> args) {
        try (Writer out = new StringWriter()) {
            render(key, source, out, args);
            return out.toString();
        } catch (IOException e) {
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 渲染
     *
     * @param key 键值
     * @param source 内容
     * @param args 参数
     * @return 渲染后模板字符串
     */
    public static String render(String key, String source, Object... args) {
        return render(key, source, Objects.map(args));
    }

    /**
     * 渲染模版文件
     *
     * @param file 文件
     * @param args 参数
     * @return 渲染后模板字符串
     */
    public static String renderFile(String file, Map<String, Object> args) {
        if (!isCached(file)) {
            String source = Files.readFile(file);
            return render(file, source, args);
        }
        return render(file, null, args);
    }

    /**
     * 渲染模版文件
     *
     * @param file 文件
     * @param args 参数
     * @return 渲染后模板字符串
     */
    public static String renderFile(String file, Object... args) {
        return renderFile(file, Objects.map(args));
    }

    /**
     * 判断是否已经缓存
     *
     * @param key 键值
     * @return 判断结果
     */
    public static Boolean isCached(String key) {
        return mJetTemplate.containsKey(key) && JetTemplateKey.get().hasKey(key);
    }

    /**
     * 清除缓存
     *
     * @param key 键值
     */
    public static void clear(String key) {
        mJetTemplate.remove(key);
        JetTemplateKey.get().removeKey(key);
        Logger.info("remove template '%s'.", key);
    }

    /** 清除所有 */
    public static void clearAll() {
        mJetTemplate.clear();
        JetTemplateKey.clear();
    }

    /**
     * @author ultrafrog
     * @version 1.0, 2017-01-13
     * @since 1.0
     */
    static class JetTemplateKey implements Serializable {

        private static final long serialVersionUID = 4522163010371700827L;
        private static final String CACHE = "cache_template";

        /**  */
        Map<String, List<String>> mapping = new HashMap<>();

        /**
         * 判断是否存在键值
         *
         * @param key 键值
         * @return 判断结果
         */
        Boolean hasKey(String key) {
            return mapping.containsKey(key) ? mapping.get(key).contains(uuid) : Boolean.FALSE;
        }

        /**
         * 添加键值
         *
         * @param key 键值
         */
        void addKey(String key) {
            if (!mapping.containsKey(key)) mapping.put(key, new ArrayList<>());
            if (!mapping.get(key).contains(uuid)) mapping.get(key).add(uuid);
            cache();
        }

        /**
         * 移除键值
         *
         * @param key 键值
         */
        void removeKey(String key) {
            if (mapping.containsKey(key)) {
                mapping.remove(key);
                cache();
            }
        }

        /** 缓存 */
        void cache() {
            Caches.set(CACHE, this);
        }

        /**
         * 从缓存读取对象
         *
         * @return 模板键值
         */
        static JetTemplateKey get() {
            JetTemplateKey jetTemplateKey = Caches.get(CACHE, JetTemplateKey.class);
            return jetTemplateKey == null ? new JetTemplateKey() : jetTemplateKey;
        }

        /** 清除缓存 */
        static void clear() {
            Caches.delete(CACHE);
        }
    }
}
